#include "clientconnection.h"
#include <QSslSocket>


ClientConnection::ClientConnection(QObject *parent) :
    QObject(parent)
{
}

/**
* @brief open secure socket connection
*
* opens a QSslSocket and connects the usual signals
*/
void ClientConnection::secureConnect(QString hostname,QString port,QString username,QString password)
{
    if (!m_socket) {
        m_socket = new QSslSocket(this);
        connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
                this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
        connect(m_socket, SIGNAL(encrypted()),
                this, SLOT(socketEncrypted()));
        connect(m_socket, SIGNAL(sslErrors(QList<QSslError>)),
                this, SLOT(sslErrors(QList<QSslError>)));
        connect(m_socket, SIGNAL(readyRead()),
                this, SLOT(socketReadyRead()));
        connect(m_socket, SIGNAL(disconnected()),
                this,SLOT(socketDisconnected()));
        connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
                this,SLOT(socketError(QAbstractSocket::SocketError)));
    }
    m_username = username;
    m_password = password;

    m_socket->connectToHostEncrypted(hostname,port.toInt());
    updateEnabledState();
}

/**
* @brief update socket state
*/
void ClientConnection::updateEnabledState()
{
    //bool unconnected = !socket || socket->state() == QAbstractSocket::UnconnectedState;

    /*
    form->hostNameEdit->setReadOnly(!unconnected);
    form->hostNameEdit->setFocusPolicy(unconnected ? Qt::StrongFocus : Qt::NoFocus);

    form->hostNameLabel->setEnabled(unconnected);
    form->portBox->setEnabled(unconnected);
    form->portLabel->setEnabled(unconnected);
    form->connectButton->setEnabled(unconnected && !form->hostNameEdit->text().isEmpty());

    bool connected = socket && socket->state() == QAbstractSocket::ConnectedState;
    form->sessionBox->setEnabled(connected);
    form->sessionOutput->setEnabled(connected);
    form->sessionInput->setEnabled(connected);
    form->sessionInputLabel->setEnabled(connected);
    form->sendButton->setEnabled(connected);
    */
}
/**
* @brief socket throw error
*/
void SslClient::socketError(QAbstractSocket::SocketError err)
{
  // err is just an enum, we get the readable error string from the socket
  QSslSocket *socket = qobject_cast<QSslSocket*>( sender() );
  Q_ASSERT( socket );

  qDebug() << "error thrown: " << socket->errorString();
  if(this->ctcdlg)
  {
    // In this case we are not yet connected, but try to
    this->ctcdlg->setErrorString(socket->errorString());
  }
  else
  {
    // Here we already had a connection up and running when the error occured. we need some cleanup
    this->statusBar()->showMessage(socket->errorString());

    // cleanup
    disconnectCore();

  }

}


/**
* @brief Socket changed state
*/
void SslClient::socketStateChanged(QAbstractSocket::SocketState state)
{
    if (executingDialog)
        return;

    updateEnabledState();
    if (state == QAbstractSocket::UnconnectedState) {
        qDebug() << "socket has gone unconnected state";
        socket->deleteLater();
        socket = 0;
    }
}


/**
* @brief slot, called when the socket established encryption.
*
* can extract cipher information here
*/
void SslClient::socketEncrypted()
{
    if (!socket)
        return;                 // might have disconnected already


    QSslCipher ciph = socket->sessionCipher();
    QString cipher = QString("%1, %2 (%3/%4)")
                            .arg(ciph.authenticationMethod())
                            .arg(ciph.name())
                            .arg(ciph.usedBits())
                            .arg(ciph.supportedBits());
}


/**
* @brief slot, called when socket has data available for reading
*
* this does the first xml parsing and decides based on the first tag it encounteres
* which subroutine should be called to parse the data
*/
void SslClient::socketReadyRead()
{

    /** Problem: Es können mehrere XML-Dokumente gleichzeitig am socket anliegen,
        die liest er dann natürlich auch alle auf einmal.
        Dadurch können zur Zeit informationen verloren gehen. Wir müssen nun
        eine Funktion schreiben, die immer bei einem <xml> trennt
        und natürlich auch nur vollständig empfangene Pakete verarbeitet. Wenn halbfertige
        empfangen werden sollten diese in einem privaten Buffer im SslClient temporär
        gespeichert werden.

        Status: Multiple Packets are parsed within one readAll
            FIXME not working: xml package split over multiple reads() (no internal buffering so far)
      */

    // retrieve socket
    QSslSocket *socket = qobject_cast<QSslSocket*>( sender() );
    Q_ASSERT( socket );


    QDomDocument domdoc;

    /** split the input read from the socket,
        each block beginning with <xml *> is parsed on its own
        This is very simple splitting but enough for our needs so far.
        It Does not take nested <xml> into account (would this be valid xml anyway?)
      */
    QString input = socket->readAll();
    QRegExp xmlsplit = QRegExp("<xml.*>");
    xmlsplit.setMinimal(true); // not greedy matching
    QStringList b = input.split(xmlsplit,QString::SkipEmptyParts);

    /** for each block of <xml> data DO */
    for (int i=0;i<b.length();i++)
    {
    domdoc.setContent(b[i]);

    // make p point to the <xml> tag
    QDomNode p = domdoc.firstChildElement();
    QDomNode proto;


    while (!p.isNull() && p.toElement().tagName() != "")
    {
        // we dont care about the <xml> tag, so strip this
        if (p.toElement().tagName()=="xml")
            proto = p.firstChildElement();
        else
            proto = p;

        QString protocol =  proto.toElement().tagName();

        if (protocol == "status")
        {
            // a buddy's status changed
            parseStatus(proto);
        }
        else if (protocol == "statechanged")
        {
            // a chat notification (XEP-0085) changed
            parseState(proto);
        }
        else if (protocol == "message")
        {
            // we received a message
            parseMessage(proto);
        }
        else if (protocol == "version")
        {
            // the core sent its version information and wants us to auth ourselfs

            QString auth_info = QString("<xml version='1.0'><auth><username>%1</username><password>%2</password></auth>").arg(this->username).arg(this->password);
            socket->write(auth_info.toUtf8()+"\r\n");
            return;
        }
        else if (protocol == "authenticated")
        {
            qDebug() << "The Core accepted our auth-info, we are recognized";

            // notify the dialog that we got the Connection up and running, so close the dlg.
            emit(connected());
        }
        else if (protocol == "buddylist")
        {
            parseBuddyList(proto);
        }
        else
        {
            qDebug() << "unrecognized or missing protocol tag: " << protocol;
        }

        p = p.nextSibling();
    }
}
    return;
}



/**
* @brief slot, called when QSslSocket raises Sslerrors
*
* when ssl errors are encountered, we display them on the debug output
* but so far, we ignore all errors and continue eitherway!
* FIXME
*/
void SslClient::sslErrors(const QList<QSslError> &errors)
{

    qDebug() << "SSL-Errors occured:";
    foreach (const QSslError &error, errors)
        qDebug() << error.errorString();


    // TODO: implement prober ssl-error handling.
    // We will accept all errors so far.
    socket->ignoreSslErrors();
}

/**
* @brief This WAS used to display cert-information, it SHOULD be later, again.
*/
void SslClient::displayCertificateInfo()
{
    // TODO
}
